package com.siyoumi.component.auth_img.impl;

import com.siyoumi.component.XRedis;
import com.siyoumi.component.auth_img.AuthImgService;
import com.siyoumi.component.auth_img.entity.AuthImgData;
import com.siyoumi.exception.EnumSys;
import com.siyoumi.component.XApp;
import com.siyoumi.util.XReturn;
import com.siyoumi.util.XStr;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;

@Data
@Slf4j
public class DragAuthImgServiceImpl extends AuthImgService {
    List<String> imgBgPathArr;
    List<String> imgHoldPathArr;
    Integer err = 10; //误差像素

    public static AuthImgService getIns(String redisKey, List<String> imgBgArr, List<String> imgHoldArr) {
        DragAuthImgServiceImpl app = new DragAuthImgServiceImpl();
        app.setRedisKey(redisKey);
        app.setImgBgPathArr(imgBgArr);
        app.setImgHoldPathArr(imgHoldArr);

        return app;
    }


    @SneakyThrows
    @Override
    public List<BufferedImage> getImgInfo(AuthImgData data) {
        BufferedImage imgBgSrc = getImgBgSrcRnd();

        int imgHoldRndIndex = getImgHoldRndIndex();
        BufferedImage imgHoldSrc = getImgHoldSrc(imgHoldRndIndex);

        int x = XApp.random(imgHoldSrc.getWidth() + 30, imgBgSrc.getWidth() - imgHoldSrc.getWidth() - 10);
        int y = XApp.random(0, imgBgSrc.getHeight() - imgHoldSrc.getHeight());
        log.debug("x: {},y: {}", x, y);

        cutHandleImg(imgBgSrc, imgHoldSrc, x, y, true);
        BufferedImage imgHold = createImgHold(imgHoldSrc, imgBgSrc.getHeight(), y);

        if (getImgHoldPathArr().size() > 1) {
            //添加1个干扰项
            int xx = XApp.random(0, imgBgSrc.getWidth() - imgHoldSrc.getHeight());
            int yy = XApp.random(0, imgBgSrc.getHeight() - imgHoldSrc.getHeight());
            int imgHoldRndIndexNodes = getImgHoldRndIndex(imgHoldRndIndex);
            cutHandleImg(imgBgSrc, getImgHoldSrc(imgHoldRndIndexNodes), xx, yy, false);
        }

        //设置缓存
        setRedisVal(String.valueOf(x));

        return List.of(imgBgSrc, imgHold);
    }

    @Override
    public XReturn auth(AuthImgData data) {
        String v = XRedis.getBean().getAndDel(getRedisKey());
        if (XStr.isNullOrEmpty(v)) {
            return EnumSys.ADMIN_AUTH_IMG_ERR.getR();
        }

        Integer clientX = Integer.valueOf(data.getCode());

        Integer sysX = Integer.valueOf(v);
        int minX = sysX - getErr();
        int maxX = sysX + getErr();
        log.debug("用户坐标X: {}", clientX);
        log.debug("系统坐标X: {}", sysX);
        log.debug("误差范围: {} - {}", minX, maxX);
        if (clientX >= minX && clientX <= maxX) {
            return EnumSys.OK.getR("验证成功");
        }

        return EnumSys.ADMIN_AUTH_IMG_ERR.getR();
    }

    private BufferedImage getImgBgSrcRnd() throws IOException {
        String imgBgPath = getImgBgPathArr().get(XApp.random(0, getImgBgPathArr().size() - 1));
        log.debug("随机图片: {}", imgBgPath);
        return ImageIO.read(new File(imgBgPath));
    }

    private int getImgHoldRndIndex() {
        return XApp.random(0, getImgHoldPathArr().size() - 1);
    }

    private int getImgHoldRndIndex(int passIndex) {
        if (getImgHoldPathArr().size() <= 1) {
            return 0;
        }

        while (true) {
            int index = XApp.random(0, getImgHoldPathArr().size() - 1);
            if (index != passIndex) {
                return index;
            }
        }
    }

    private BufferedImage getImgHoldSrc(int index) throws IOException {
        String imgHoldPath = getImgHoldPathArr().get(index);
        return ImageIO.read(new File(imgHoldPath));
    }

    /**
     * 生成方块图片
     *
     * @param imgHold 方块
     * @param rndY    随机y
     */
    @SneakyThrows
    public BufferedImage createImgHold(BufferedImage imgHold, int bgHeigth, Integer rndY) {
        Integer width = imgHold.getWidth();
        Integer height = bgHeigth;
        //图片
        //TYPE_INT_ARGB 创建一个带透明色的对象
        //TYPE_INT_RGB 创建一个不带透明色的对象
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
//        g.clearRect(0, 0, width, height); //清空
//        g.setColor(Color.WHITE);//设置背景色
//        g.setComposite(AlphaComposite.Clear); //设置背景透明
//        g.fillRect(0, 0, width, height);//填充背景
//        int rnd = XApp.random(-50, 50);
        g.drawImage(imgHold, 0, rndY, imgHold.getWidth(), imgHold.getHeight(), null);

        g.dispose();

        return image;
    }

    /**
     * 添加干扰
     *
     * @param imgBg
     * @param imgHold
     */
    public void cutHandleImg(BufferedImage imgBg, BufferedImage imgHold, int rndX, int rndY, boolean setHold) {
        //临时数组遍历用于高斯模糊存周边像素值
        int[][] martrix = new int[3][3];
        int[] values = new int[9];

        int xLength = imgHold.getWidth();
        int yLength = imgHold.getHeight();
        // 模板图像宽度
        for (int x = 0; x < xLength; x++) {
            // 模板图片高度
            for (int y = 0; y < yLength; y++) {
                // 如果模板图像当前像素点不是透明色 copy源文件信息到目标图片中
                int rgb = imgHold.getRGB(x, y);
                if (rgb >= 0) {
                    continue;
                }

                //占位符图片白色rgb视为边界
                boolean isBroder = Color.black.getRGB() != rgb; //描边
                try {
                    if (isBroder) {
                        //边框设置白色
                        imgBg.setRGB(rndX + x, rndY + y, Color.white.getRGB());
                    } else {
                        if (setHold) {
                            int bgRGB = imgBg.getRGB(rndX + x, rndY + y);
                            //占位图赋值
                            imgHold.setRGB(x, y, bgRGB);
                        }

                        //抠图区域高斯模糊
                        readPixel(imgBg, rndX + x, rndY + y, values);
                        fillMatrix(martrix, values);
//                        imgBg.GaussianBlur()
                        imgBg.setRGB(rndX + x, rndY + y, avgMatrix(martrix));
                    }
                } catch (Exception ex) {
                    log.debug("xl: {},yl: {}", xLength, yLength);
                    log.debug("x: {},y: {}", rndX, rndY);
                    log.debug("i: {},j: {}", rndX + x, rndY + y);

                    throw ex;
                }

            }
        }
    }

    private static int avgMatrix(int[][] matrix) {
        int r = 0;
        int g = 0;
        int b = 0;
        for (int i = 0; i < matrix.length; i++) {
            int[] x = matrix[i];
            for (int j = 0; j < x.length; j++) {
                if (j == 1) {
                    continue;
                }
                Color c = new Color(x[j]);
                r += c.getRed();
                g += c.getGreen();
                b += c.getBlue();
            }
        }
        return new Color(r / 8, g / 8, b / 8).getRGB();
    }

    private static void fillMatrix(int[][] matrix, int[] values) {
        int filled = 0;
        for (int i = 0; i < matrix.length; i++) {
            int[] x = matrix[i];
            for (int j = 0; j < x.length; j++) {
                x[j] = values[filled++];
            }
        }
    }

    private static void readPixel(BufferedImage img, int x, int y, int[] pixels) {
        int xStart = x - 1;
        int yStart = y - 1;
        int current = 0;
        for (int i = xStart; i < 3 + xStart; i++) {
            for (int j = yStart; j < 3 + yStart; j++) {
                int tx = i;
                if (tx < 0) {
                    tx = -tx;

                } else if (tx >= img.getWidth()) {
                    tx = x;
                }
                int ty = j;
                if (ty < 0) {
                    ty = -ty;
                } else if (ty >= img.getHeight()) {
                    ty = y;
                }
                pixels[current++] = img.getRGB(tx, ty);

            }
        }
    }
}
